/*----------------------------------------------------------------------------------------------
 *
 * This file is XIU's property. It contains XIU's trade secret, proprietary and
 * confidential information.
 *
 * The information and code contained in this file is only for authorized XIU employees
 * to design, create, modify, or review.
 *
 * DO NOT DISTRIBUTE, DO NOT DUPLICATE OR TRANSMIT IN ANY FORM WITHOUT PROPER AUTHORIZATION.
 *
 * If you are not an intended recipient of this file, you must not copy, distribute, modify,
 * or take any action in reliance on it.
 *
 * If you have received this file in error, please immediately notify XIU and
 * permanently delete the original and any copy of any file and any printout thereof.
 * (c) www.xiusdk.cn
 *---------------------------------------------------------------------------------------------*/

package com.samples;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import com.inartar.effect.Constraints;
import com.utils.Accelerometer;

import java.io.IOException;
import java.util.List;
/**
 * Created by xiu on 2017/7/18.
 */

public abstract class CameraActivity extends Activity implements SurfaceHolder.Callback, VideoRenderer.Callback{
    // Camera Views
    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private Camera camera;
    private GLSurfaceView glSurfaceView;
    private boolean hasSurface = false;
    private int WIDTH_RES = 1280;
    private int HEIGHT_RES = 720;
    private int cameraId = android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT;
    private VideoRenderer mRenderer;
    Accelerometer acc;

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_camera);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        View switchCameraBtn = (View) findViewById(R.id.switchCamera);
        switchCameraBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (Camera.getNumberOfCameras() > 1) {
                    if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                        cameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
                    } else {
                        cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
                    }
                    startCamera();
                }
            }
        });

        glSurfaceView = (GLSurfaceView) findViewById(R.id.glview);
        surfaceView = (SurfaceView) findViewById(R.id.sv_capture_preview);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mRenderer = new VideoRenderer(this, this, glSurfaceView);
        glSurfaceView.setZOrderMediaOverlay(true);
        try {
            openCamera();

            if (camera != null) {
                android.hardware.Camera.Size size = camera.getParameters().getPreviewSize();
                WIDTH_RES = size.width;
                HEIGHT_RES = size.height;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        if (acc == null) {
            acc = new Accelerometer(this);
            acc.start();
        }

        if (!hasSurface)
            surfaceHolder.addCallback(this);
        else {
            startCamera();
        }
        glSurfaceView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();

        stopCamera();

        glSurfaceView.onPause();
        if (acc != null) {
            acc.stop();
            acc = null;
        }
    }

    @Override
    public void onDestroy() {

        super.onDestroy();
    }

    private void startCamera() {
        stopCamera();

        try
        {
            openCamera();
            if (camera != null) {

                try {
                    setupCamera();
                    camera.setPreviewCallbackWithBuffer(mRenderer);
                    camera.setPreviewDisplay(surfaceHolder);
                    camera.startPreview();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                hasSurface = true;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

    }

    private void stopCamera() {
        if (camera == null)
            return;

        camera.setPreviewCallback(null);
        camera.stopPreview();
        camera.release();
        camera = null;
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    private void openCamera() {

        if (null == camera) {

            try {
                camera = Camera.open(cameraId);
            } catch (Exception e) {
                camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
                cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            }

            if (camera != null) {
                // 1. try to set preview size
                Camera.Parameters parameters = camera.getParameters();

                Camera.Size preivewSize = getOptimalPreviewSize(
                        parameters.getSupportedPreviewSizes(), WIDTH_RES,
                        HEIGHT_RES);
                parameters.setPreviewSize(preivewSize.width,
                        preivewSize.height);
                WIDTH_RES = preivewSize.width;
                HEIGHT_RES = preivewSize.height;

                ///* 防止出现条纹（小米手机的auto反条纹模式有问题） */
                List<String> supportAnti = parameters.getSupportedAntibanding();
                final String needAntiMode = Camera.Parameters.ANTIBANDING_50HZ;
                if (null != supportAnti && supportAnti.contains(needAntiMode)) {
                    parameters.setAntibanding(needAntiMode);
                }

                try {
                    camera.setParameters(parameters);
                } catch (Exception e) {
                    // 2. failed to set preview size
                    parameters = camera.getParameters();
                }

                List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
                int minFps = Integer.MAX_VALUE;
                int maxFps = 0;
                if (fpsList.size() > 0) {
                    for (int[] fpsRange : fpsList) {
                        minFps = Math.min(minFps, fpsRange[0]);
                        maxFps = Math.max(maxFps, fpsRange[1]);
                    }
                    // if(maxFps > 15000) // it supports it
                    // parameters.setPreviewFpsRange(maxFps, maxFps);
                }
                try {
                    camera.setParameters(parameters);
                } catch (Exception e) {
                    // 2. failed to set fps range
                    parameters = camera.getParameters();
                }

                try {
                    // some devices, such as Huawei P6, will crash for it
                    parameters.setPreviewFormat(ImageFormat.NV21);
                    // focus.
                    //parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                    camera.setParameters(parameters);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void runOnDraw(final Runnable runnable)
    {
        mRenderer.runOnDraw(runnable);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startCamera();
    }

    private void setupCamera() {
        Camera.Size size = camera.getParameters().getPreviewSize();
        camera.addCallbackBuffer(new byte[size.width * size.height * 3 / 2]);
        camera.addCallbackBuffer(new byte[size.width * size.height * 3 / 2]);
        camera.addCallbackBuffer(new byte[size.width * size.height * 3 / 2]);

        int rotation = this.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
            case Surface.ROTATION_0:
            default:
                degrees = 0;
                break;
        }
        //camera.setDisplayOrientation();
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            degrees = (info.orientation + degrees) % 360;
        } else {
            degrees = (info.orientation + 360 - degrees) % 360;
        }
        int orientation = Constraints.YN_CLOCKWISE_ROTATE_0;
        switch (degrees) {
            case 90:
                orientation = Constraints.YN_CLOCKWISE_ROTATE_90;
                break;
            case 180:
                orientation = Constraints.YN_CLOCKWISE_ROTATE_180;
                break;
            case 270:
                orientation = Constraints.YN_CLOCKWISE_ROTATE_270;
                break;
            default:
                break;
        }
        this.mRenderer.setupCamera(orientation, cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera = null;
        }
        hasSurface = false;
    }

}
